package tester.component;

import tester.entitys.JdbcInfo;
import tester.entitys.TableInfo;

import java.io.*;
import java.sql.*;
import java.sql.Date;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class Loader {
    public static final String prefix = "Loader";
    public static void load(String fileName) {
        // 1. 解析导出文件
        Map<String, List<String[]>> tableDataMap;
        String exportFilePath = Saver.getFilePath(fileName);
        try {
            tableDataMap = parseExportFile(exportFilePath);
            System.out.println("已解析 "+exportFilePath+"  " + tableDataMap.size() + " 个表数据");
        } catch (IOException e) {
            System.err.println("解析导出文件失败: " + e.getMessage());
            return;
        }

        // 2. 初始化数据库连接

        try  {
            Class.forName("oracle.jdbc.driver.OracleDriver");
            Connection conn = null;
            JdbcInfo nowInfo = null;
            // 3. 按表恢复数据
            for (Map.Entry<String, List<String[]>> entry : tableDataMap.entrySet()) {
                String[] split = entry.getKey().split("\\.");
                String signature = split[0];
                String tableName = split[1];
                JdbcInfo info = Configer.JdbcMap.get(signature);
                if (nowInfo == null || !info.getSign().equals(nowInfo.getSign())) {
                    conn = DriverManager.getConnection(info.getUrl(), info.getUser(), info.getPwd());
                    conn.setAutoCommit(false); // 使用事务
                    nowInfo = info;
                }
                List<String[]> tableData = entry.getValue();

                // 先TRUNCATE表
                truncateTable(conn, tableName);
                if (tableData.size() < 3) {
                    System.out.println("表 " + tableName + " 为null");
                    continue;
                }
                // 第一行是列头 第二行是列类型
                String[] columns = tableData.get(0);
                String[] columnTypes = tableData.get(1);
                List<String[]> rows = tableData.subList(2, tableData.size());

                restoreTable(conn, tableName, columns,columnTypes, rows);
            }

            conn.commit();
            System.out.println("数据恢复完成!");
        } catch (SQLException | ClassNotFoundException e) {
            System.err.println("数据库操作失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
    private static void truncateTable(Connection conn, String tableName) throws SQLException {
        // 使用TRUNCATE TABLE语句更高效
        try (Statement stmt = conn.createStatement()) {
            // 禁用外键约束检查（如有）
            try {
                stmt.execute("SET FOREIGN_KEY_CHECKS = 0");
            } catch (SQLException e) {
                // 不是所有数据库都支持这个语法，忽略错误
            }

            // 执行TRUNCATE
            System.out.println("清空表: " + tableName);
            stmt.execute("TRUNCATE TABLE " + tableName);

            // 重新启用外键约束检查
            try {
                stmt.execute("SET FOREIGN_KEY_CHECKS = 1");
            } catch (SQLException e) {
                // 忽略错误
            }
        }
    }
    private static void restoreTable(Connection conn, String tableName,
                                     String[] columns,String[] columnTypes, List<String[]> rows) throws SQLException {
        // 构建INSERT语句
        String placeholders = String.join(",", Collections.nCopies(columns.length, "?"));
        String sql = String.format("INSERT INTO %s (%s) VALUES (%s)",
                tableName, String.join(",", columns), placeholders);

        System.out.println("正在恢复表: " + tableName + "，共 " + rows.size() + " 条记录");

        try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
            int batchSize = 0;
            for (String[] row : rows) {
                for (int i = 0; i < row.length; i++) {
                    int sqlType = getSqlType(columnTypes[i]);
                    String value = row[i];
                    String columnName = columns[i];


                    // 根据类型设置参数
                    try {
                        setParameterByType(pstmt, i + 1, sqlType, value==null?null:value.trim());
                    } catch (SQLException | IllegalArgumentException e) {
                        System.err.printf("表 %s 列 %s 行 %d 值 '%s' 转换失败: %s%n",
                                tableName, columnName, batchSize + 1, value, e.getMessage());
                        pstmt.setNull(i + 1, sqlType); // 转换失败设为NULL
                    }
                }
                pstmt.addBatch();

                // 分批执行
                if (++batchSize % 100 == 0) {
                    pstmt.executeBatch();
                }
            }
            pstmt.executeBatch(); // 执行剩余批次
        }
    }

    /**
     * 根据SQL类型设置参数值
     */
    private static void setParameterByType(PreparedStatement pstmt,
                                           int paramIndex,
                                           int sqlType,
                                           String value) throws SQLException {
        if (value == null || value.isEmpty()) {
            pstmt.setNull(paramIndex, sqlType);
            return;
        }

        try {
            switch (sqlType) {
                case Types.DOUBLE:
                    pstmt.setDouble(paramIndex, Double.parseDouble(value));
                    break;

                case Types.DATE:
                    pstmt.setDate(paramIndex, new Date(Timestamp.valueOf(value).getTime()));
                    break;

                case Types.TIMESTAMP:
                    pstmt.setTimestamp(paramIndex, Timestamp.valueOf(value));
                    break;

                default: // VARCHAR, CHAR, etc.
                    pstmt.setString(paramIndex, value);
            }
        } catch (Exception e) {
            throw new SQLException("无法将值 '" + value + "' 转换为类型 " + sqlType, e);
        }
    }



    private static final Pattern TABLE_PATTERN = Pattern.compile("^=== (.+) ===$");
    public static Map<String, List<String[]>> parseExportFile(String filePath) throws IOException {
        Map<String, List<String[]>> tableDataMap = new LinkedHashMap<>(); // 保持表顺序
        List<String[]> currentTableData = null;
        String currentTableName = null;
        String[] currentColumns = null;

        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                line = line.trim();

                // 检查是否为新表开始
                Matcher tableMatcher = TABLE_PATTERN.matcher(line);
                if (tableMatcher.matches()) {
                    // 保存前一个表的数据
                    if (currentTableName != null && currentTableData != null) {
                        tableDataMap.put(currentTableName, currentTableData);
                    }

                    // 开始新表
                    currentTableName = tableMatcher.group(1).trim();
                    currentTableData = new ArrayList<>();
                    currentColumns = null;
                    continue;
                }

                // 忽略空行
                if (line.isEmpty()) {
                    continue;
                }

                // 处理数据行
                if (currentTableName != null) {
                    String[] fields = line.split("\\|", -1); // -1保留空字段

                    if (currentColumns == null) {
                        // 第一行非空行是列头
                        currentColumns = fields;
                        currentTableData.add(fields); // 把列头也保存下来
                    } else {
                        // 处理数据行中的特殊值
                        for (int i = 0; i < fields.length; i++) {
                            fields[i] = fields[i].trim();
                            if (fields[i].equalsIgnoreCase("NULL")) {
                                fields[i] = null;
                            }
                        }
                        currentTableData.add(fields);
                    }
                }
            }

            // 添加最后一个表的数据
            if (currentTableName != null && currentTableData != null) {
                tableDataMap.put(currentTableName, currentTableData);
            }
        }

        return tableDataMap;
    }
    public static String getPstmtSetType(String str){
        if (str.equals("NUMBER")) return "BigDecimal";
        if (str.equals("VARCHAR2")) return "String";
        if (str.equals("DATE")) return "DATE";
        if (str.equals("TIMESTAMP")) return "Timestamp";
        return str;
    }
    private static int getSqlType(String str) {
        if (str.equals("NUMBER")) return Types.DOUBLE;
        if (str.equals("VARCHAR2")) return Types.VARCHAR;
        if (str.equals("DATE")) return Types.DATE;
        if (str.equals("TIMESTAMP")) return Types.TIMESTAMP;
        return Types.VARCHAR;
    }
}
